#include "atom.h"

// Default constructor that returns a floating atom at 0,0,0 in the A sublattice.
Atom::Atom() : pos(arma::fill::ones), sublatt(A) { }
// Overload constructor that returns a floating atom at i,j,h in the sublattice l.
Atom::Atom(const long &iv, const long &jv, const long &hv, const long &lv) : pos(), sublatt((lattice)lv) { pos[0] = iv; pos[1] = jv; pos[2] = hv; }
// Overload constructor that returns a floating atom at v1,v2,v3 in the sublattice v4.
Atom::Atom(const ivec4 &v) : pos(v.subvec(0,2)), sublatt((lattice)v[3]) { }
	
// Getter function that returns the position of the atom in the respective sublattice.
const ivec3 &Atom::position() const { return pos; }
// Getter function that returns the respective sublattice.
const Atom::lattice &Atom::sublattice() const { return sublatt; }
// Getter function that returns the position and the respective sublattice of the atom in a 4D vector.
ivec4 Atom::toIVec4() const { ivec4 result; result.subvec(0,2) = pos; result(3) = (long)sublatt; return result; }
	
// Convenience getter function that returns the first coordinate of position().
long Atom::i() const { return pos(0); }
// Convenience getter function that returns the second coordinate of position().
long Atom::j() const { return pos(1); }
// Convenience getter function that returns the third coordinate of position().
long Atom::h() const { return pos(2); }
// Convenience getter function that returns the sublattice as a long type (0 is A and 1 is B).
long Atom::l() const { return (long)sublatt; }
	
// Getter function the returns the atoms type.
Atom::type Atom::getType() const { return atomtype; }
// Setter function the sets the atoms type (when neihbours are found the type can be set accordingly).
void Atom::setType(const type &t) { atomtype = t; }
bool Atom::isDimer() const { return atomtype == dimer; }

// Getter function the transforms the atoms position in lattice coordinates to x,y,z coordinates, cc-bonds are parallel to y-direction.
vec3 Atom::xyz() const { vec3 result; result(0) = x(); result(1) = y(); result(2) = z(); return result; }
// Getter function the transforms the atoms position in lattice coordinates to x,y coordinates, cc-bonds are parallel to y-direction.
vec2 Atom::xy() const { vec2 result; result(0) = x(); result(1) = y(); return result; }
// Getter function the transforms the atoms position in lattice coordinates to the x coordinate, cc-bonds are parallel to y-direction.
double Atom::x() const 
{ 
	// The corresponding real space coordinates are well known. The h-vector has zero in x-direction.
	static const double dX = 0.5; // A precalculated value for convenience and speed.

	return (i()+j())*dX;
}
// Getter function the transforms the atoms position in lattice coordinates to the y coordinate, cc-bonds are parallel to y-direction.
double Atom::y() const
{ 
	// The corresponding real space coordinates are well known. 
	static const double dY = 0.5*sqrt(3.0);		// A precalculated value for convenience and speed.
	static const double ddY = sqrt(1.0/3.0); 	// A precalculated value for convenience and speed.

	return double(i()-j())*dY+double(h()+l())*ddY;
}
// Getter function the transforms the atoms position in lattice coordinates to the z coordinate, cc-bonds are parallel to y-direction.
double Atom::z() const
{ 
	return double(h());
}
	
// Static evaluation function to find the relative distance between two atoms, used for bond determination.
ivec4 Atom::difference(const Atom &a, const Atom &n) { return n.toIVec4()-a.toIVec4(); }
// Static helper function.
vec3 Atom::changeBasis(const ivec4 &v)
{
	static const mat basischange = basisChangeMat();
	
	return basischange*v;
}
// Static helper function.
mat Atom::changeBasis(const imat &m)
{
	static const mat basischange = basisChangeMat();
	
	return basischange*m;
}
// Convenience function initiate the basis change matrix in toXyz().
mat Atom::basisChangeMat()
{
	static double sqrtthreehalf = sqrt(3.)/2.;
	static double invsqrtthree = 1./(sqrt(3.));

	mat result;

	//			i-vector:		j-vector:			h-vector:		l-vector:
	result  << 0.5			<< 0.5				<< 0.			<< 0.				<< arma::endr  // x-coordinates
			<< sqrtthreehalf<< -sqrtthreehalf	<< invsqrtthree	<< invsqrtthree		<< arma::endr  // y-coordinates
			<< 0.			<< 0.				<< 1.			<< 0.				<< arma::endr; // z-coordinates

	return result;
}